package cytargetlinker.conversion;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import org.bridgedb.IDMapperStack;

import cytargetlinker.conversion.ArgsParser.AFilesAttributes;
import cytargetlinker.conversion.ArgsParser.AFilesIn;
import cytargetlinker.conversion.ArgsParser.AFilesOut;
import cytargetlinker.conversion.ArgsParser.AHelp;
import cytargetlinker.conversion.ArgsParser.GraphBuilder;
import cytargetlinker.conversion.graph.Graph;
import cytargetlinker.conversion.graph.Graph.Edge;

/**
 * Converts miRNA target text files from MiRTarBase to a XGMML or GML network.
 * 
 * The input files for this script can be downloaded here:
 * http://mirtarbase.mbc.nctu.edu.tw/php/download.php (under
 * "Catatolog by Species").
 * 
 * @author Thomas, Pooja, Naime, Tina
 */
public class MirTarBase {
	
	private final static Logger log = Logger.getLogger(MirTarBase.class.getName());
	static Args pargs;

	private interface Args extends AHelp, AFilesIn, AFilesOut, AFilesAttributes {}

	/**
	 * MAIN METHOD
	 * USAGE: java -cp conversion.jar cytargetlinker.conversion.MirTarBase 
	 * ARGUMENTS: 
	 * -i = input file
	 * -o = output file
	 * --bridgeDbSource = BridgeDb mapping file for the source nodes (gene database)
	 * --bridgeDbTarget = BridgeDb mapping file for the target nodes (miRBase database)
	 * --organism "Mus musculus" or "Homo sapiens" 
	 */
	public static void main(String argv[]) throws Exception {
		pargs = ArgsParser.parse(argv, Args.class);
		MirTarBase converter = new MirTarBase();
		converter.startConversion();
	}

	private Graph graph;
	private Map<String, Integer> index;
	private Map<String, List<String>> edges;
	private IDMapperStack gdb;

	private List<String> foundConnections;
	private Integer countEdges = 0;
	private boolean mapping = true;

	public MirTarBase() {
		foundConnections = new ArrayList<String>();
		index = new HashMap<String, Integer>();
		edges = new HashMap<String, List<String>>();
	}

	public void startConversion() throws Exception {
		File in = pargs.getInput().get(0);
		if(in != null) {
			Utils.setUpLogger(log, in, false);
			log.info("conversion of miRTarBase file started ...\n");
			gdb = Utils.initIDMapper(pargs.getBridgeDbFileSource(), pargs.getBridgeDbFileTarget());
			
			if (gdb == null) {
				mapping = false;
				log.info("no identifier mapping");
			}
			if (pargs.getOrganism() != null) {
				ArgsParser.convertAndWrite(pargs, pargs, new GraphBuilder() {
					public Graph buildGraph(File in) throws Exception {
						return importMiRecords(in);
					}
				});
			} else {
				log.severe("Please specify organism. --organism Homo sapiens or Mus musculus.\n");
			}
			log.info("miRTarBase network file created.\n\n");
		} else {
			System.out.println("Please specify input file!");
		}
	}

	public Graph importMiRecords(File input) throws IOException {
		foundConnections = new ArrayList<String>();

		BufferedReader br = new BufferedReader(new FileReader(input));

		String[] header = br.readLine().split("\t");
		index = new HashMap<String, Integer>();
		for (int i = 0; i < header.length; i++) {
			index.put(header[i], i);
		}

		graph = new Graph();
		setNetworkAttributes(input);

		String line = null;
		List<String[]> rows = new ArrayList<String[]>();
		while ((line = br.readLine()) != null) {
			String[] str = line.split("\t");
			if (str[index.get("Species (Target Gene)")].equals(pargs .getOrganism())) {
				rows.add(line.split("\t", header.length));
			}
		}

		edges = new HashMap<String, List<String>>();
		for (String[] r : rows) {
			
			String geneId = r[index.get("Target Gene (Entrez ID)")];
			createGeneNode(r, geneId);

			String mirna = r[index.get("miRNA")];
			createMiRNANode(r, mirna);

			addEdge(geneId, mirna, r);
		}

		log.info(foundConnections.size() + " interactions have been found.\n");

		return graph;
	}

	private void createMiRNANode(String [] row, String id) {
		Map<String, String> attrMirna = new HashMap<String, String>();
		attrMirna.put("miRNA", id);
		attrMirna.put("label", id);
		attrMirna.put("name", id);
		attrMirna.put("Organism", row[index.get("Species (miRNA)")]);

		if(!mapping) {
			Utils.createNode(graph, id, attrMirna);
		} else {
			List<String> sysCodeOut2 = new ArrayList<String>();
			sysCodeOut2.add("Mbm");
			sysCodeOut2.add("Mb");
	
			Utils.createNode(graph, id, "Mb", sysCodeOut2, attrMirna, gdb);
		}
	}
	
	private void createGeneNode(String [] row, String id) {
		Map<String, String> attrGene = new HashMap<String, String>();
		attrGene.put("label", row[index.get("Target Gene")]);
		attrGene.put("name", row[index.get("Target Gene")]);
		attrGene.put("Organism", row[index.get("Species (Target Gene)")]);

		if(!mapping) {
			Utils.createNode(graph, id, attrGene);
		} else {
			List<String> sysCodeOut = new ArrayList<String>();
			if(pargs.getOrganism().equals("Homo sapiens")) sysCodeOut.add("EnHs");
			if(pargs.getOrganism().equals("Mus musculus")) sysCodeOut.add("EnMm");
			sysCodeOut.add("H");
			sysCodeOut.add("L");
	
			Utils.createNode(graph, id, "L", sysCodeOut, attrGene, gdb);
		}
	}
	
	private void setNetworkAttributes(File input) {
		graph.setTitle("MirTarBase");
		graph.setAttribute(CommonAttributes.DATABASE.getName(), "MirTarBase");
		graph.setAttribute(CommonAttributes.TYPE.getName(), "miRNA targets");
		graph.setAttribute(CommonAttributes.IDENTIFIERS.getName(), "identifiers");
		graph.setAttribute(CommonAttributes.SOURCE_FILE.getName(), input.getName());
		graph.setAttribute(CommonAttributes.TARGET_DATASOURCE.getName(), "Entrez Gene");
		graph.setAttribute(CommonAttributes.SOURCE_DATASOURCE.getName(), "miRBase");
		graph.setAttribute(CommonAttributes.SOURCE_TYPE.getName(), "mirna");
		graph.setAttribute(CommonAttributes.TARGET_TYPE.getName(), "gene");
	}

	private void addEdge(String gene, String mirna, String[] r) {
		if (edges.containsKey(gene)) {
			if (!edges.get(gene).contains(mirna)) {
				Edge e = graph.addEdge("" + countEdges, graph.getNode(mirna), graph.getNode(gene));
				e.setAttribute("experiments", r[index.get("Experiments")]);
				e.setAttribute("supportType", r[index.get("Support Type")]);
				e.setAttribute("referenceID", r[index.get("References (PMID)")]);
				edges.get(gene).add(mirna);
				foundConnections.add(gene + "\t" + mirna);
				countEdges++;
			}
		} else {
			Edge e = graph.addEdge("" + countEdges, graph.getNode(mirna), graph.getNode(gene));
			e.setAttribute("experiments", r[index.get("Experiments")]);
			e.setAttribute("supportType", r[index.get("Support Type")]);
			e.setAttribute("referenceID", r[index.get("References (PMID)")]);

			List<String> list = new ArrayList<String>();
			list.add(mirna);
			foundConnections.add(gene + "\t" + mirna);
			edges.put(gene, list);
			countEdges++;
		}
	}
}
